// Package utils
// @Author: zhangdi
// @File: dto
// @Version: 1.0.0
// @Date: 2023/5/18 10:15
package utils

import (
	"fmt"
	"reflect"
)

// DtoToEntity 将 DTO 转换成实体对象
func DtoToEntity(dto interface{}, entity interface{}) {
	// 获取Dto对象和Entity对象的反射值
	dtoValue := reflect.ValueOf(dto).Elem()       // dto必须传入一个指针类型
	entityValue := reflect.ValueOf(entity).Elem() // entity必须传入一个指针类型

	// 遍历Dto对象的每个字段并转换
	for i := 0; i < dtoValue.NumField(); i++ { // NumField返回结构体中字段的数量
		// 获取Dto对象的字段值
		dtoField := dtoValue.Field(i)

		// 如果Dto对象的字段值不为空，则进行转换
		if !dtoField.IsZero() {
			// 获取Dto对象的字段名
			fieldName := dtoValue.Type().Field(i).Name

			// 根据字段名获取Entity对象的同名字段
			entityField := entityValue.FieldByName(fieldName)

			// 若找到同名字段且类型相同，将Dto对象的值复制给Entity对象
			if entityField.IsValid() && entityField.Type() == dtoField.Type() {
				entityField.Set(dtoField)
			}
		}
	}
}

// DtoToEntityArr 将 DTO 切片转换成实体对象切片
func DtoToEntityArr(dtos []any, entities []any) error {
	if len(dtos) != len(entities) {
		return fmt.Errorf("dtos and entities slices must have the same length")
	}

	for i := range dtos {
		dtoValue := reflect.ValueOf(dtos[i])
		if dtoValue.Kind() != reflect.Ptr || dtoValue.IsNil() {
			return fmt.Errorf("dto elements must be non-nil pointers to structs")
		}
		entityValue := reflect.ValueOf(entities[i])
		if entityValue.Kind() != reflect.Ptr || entityValue.IsNil() {
			return fmt.Errorf("entity elements must be non-nil pointers to structs")
		}

		dtoValue = dtoValue.Elem()
		entityValue = entityValue.Elem()

		if dtoValue.Type().NumField() != entityValue.Type().NumField() {
			return fmt.Errorf("dto and entity must have the same number of fields")
		}

		// 遍历DTO对象的每个字段并转换
		for j := 0; j < dtoValue.NumField(); j++ {
			dtoField := dtoValue.Field(j)
			if !dtoField.IsZero() {
				fieldName := dtoValue.Type().Field(j).Name
				entityField := entityValue.FieldByName(fieldName)
				if entityField.IsValid() && entityField.Type() == dtoField.Type() {
					entityField.Set(dtoField)
				}
			}
		}
	}
	return nil
}
